
#ifndef _DS_STRING_H_
#define _DS_STRING_H_

#pragma once

#include <string.h>
//#include "ap\ap_proc_lock.h"

template <class CH>
struct dsTSTRING_BUF_HEADER {
   int      refCount;
   int      strLen;
   int      maxStrLen;         // number of CH's str[] can accept (not include leading 0)
   //int      locked;
#ifdef __PLUGIN__
   typedef void * (dsTSTRING_BUF_HEADER:: * DELETER) (void *, int);
   void *   Realloc            (void * p, int size) { return realloc(p, size); }
   DELETER  deleter;
#endif
   CH       str[1];
};

template <class CH>
class dsTSTRING {
//   AP_DECLARE_BINDABLE_TO_PROCESSOR;

public:
            dsTSTRING         (void);
//   explicit dsTSTRING         (apSUPPRESS_PROCESSOR_LOCK);
   explicit dsTSTRING         (int reserve);
            dsTSTRING         (const dsTSTRING &str);
//            dsTSTRING         (const dsTSTRING &str, apSUPPRESS_PROCESSOR_LOCK);
            dsTSTRING         (CH ch, int count);
            dsTSTRING         (const CH *str, int strLen = -1, int addStrLen = 0);
//            dsTSTRING         (const CH *str, apSUPPRESS_PROCESSOR_LOCK, int strLen = -1, int addStrLen = 0);
           ~dsTSTRING         (void);

   const dsTSTRING&  operator =     (const dsTSTRING &str);
   const dsTSTRING&  operator =     (CH ch) { Clear(); *this += ch; return *this; }

   const CH*         CStr           (void) const                           { return pBuffer->str; }
   CH*               CHStr          (void) const                           { return pBuffer->str; }
   int               Length         (void) const                           { return pBuffer->strLen; }
   bool              IsEmpty        (void) const                           { return pBuffer->strLen == 0; }
   void              Reserve        (int strLen);
   void              Compact        (bool noDup = true);
   
   CH*               Lock           (int strLenReq);  // not include leading 0
   int               GetLockSize    (void)                                 { return pBuffer->maxStrLen; }
   void              Unlock         (void);

   bool              operator ==    (const dsTSTRING &str) const;
   bool              operator ==    (const CH *str) const;
   bool              operator !=    (const dsTSTRING &str) const;
   bool              operator !=    (const CH *str) const;
   bool              operator >     (const dsTSTRING &str) const;
   bool              operator >     (const CH *str) const;
   bool              operator <     (const dsTSTRING &str) const;
   bool              operator <     (const CH *str) const;
   bool              operator >=    (const dsTSTRING &str) const;
   bool              operator >=    (const CH *str) const;
   bool              operator <=    (const dsTSTRING &str) const;
   bool              operator <=    (const CH *str) const;

   void              operator +=    (const dsTSTRING &str)                 { Insert(Length(), str); }
   void              operator +=    (const CH *str)                        { Insert(Length(), str); }
   void              operator +=    (CH chr)                               { Insert(Length(), chr, 1); }

   CH                operator []    (int idx) const                        { STRONG_ASSERT(idx >= 0 && idx <= pBuffer->strLen); return pBuffer->str[idx]; }
   CH&               At             (int idx);

   dsTSTRING         SubStr         (int pos, int num) const;
   dsTSTRING         Lower          (void) const;
   void              ToLower        (void);
   dsTSTRING         Upper          (void) const;
   void              ToUpper        (void);

   bool              IsStartWith    (const CH *s, bool noCase = false, int ofs = 0) const;
   bool              IsEndWith      (const CH *s, bool noCase = false) const;
   bool              IsStartWith    (const dsTSTRING &s, bool noCase = false, int ofs = 0) const { return IsStartWith(s.CStr(), noCase, ofs); }
   bool              IsEndWith      (const dsTSTRING &s, bool noCase = false) const { return IsEndWith(s.CStr(), noCase); }

   dsTSTRING         ToTrimmed      (void) const { dsTSTRING res = *this; res.Trim(); return res; }
   void              Trim           (void);
   void              TrimRight      (void);
   void              TrimLeft       (void);

   dsTSTRING         EscForm        (void);
   dsTSTRING         UnEscForm      (void);

   dsTSTRING&        Clear          (void);
   dsTSTRING&        Erase          (int pos, int num);
   dsTSTRING&        Insert         (int pos, const dsTSTRING &str);
   dsTSTRING&        Insert         (int pos, const CH *str, int len = -1);
   dsTSTRING&        Insert         (int pos, CH c, int num);
   
   int               Find           (CH c, int startPos = 0, BOOL ignoreCase = FALSE) const;
   int               Find           (const CH *str, int startPos = 0, BOOL ignoreCase = FALSE) const;
   int               Find           (const dsTSTRING &str, int startPos = 0, BOOL ignoreCase = FALSE) const;
   int               RFind          (CH c, int start = -1, BOOL ignoreCase = FALSE) const;
   int               RFind          (const CH* str, int start = -1, BOOL ignoreCase = FALSE) const;
   typedef dsTSTRING_BUF_HEADER<CH>    BUFFER;

   int               Replace        (const dsTSTRING<CH> & key, const dsTSTRING<CH> & value);

   dsTSTRING         LJust          (int width) const;
   dsTSTRING         RJust          (int width) const;

   // Arg returns a copy of this string with the lowest numbered place marker replaced by string a, i.e., %1, %2, ..., %99.
   // fieldWidth specifies the minimum amount of space that argument a shall occupy. 
   // If a requires less space than fieldWidth, it is padded to fieldWidth with character fillChar. 
   // A positive fieldWidth produces right-aligned text. A negative fieldWidth produces left-aligned text.
   dsTSTRING         Arg            (const dsTSTRING &a, int fieldWidth = 0, char fillChar = ' ') const;
   dsTSTRING         Arg            (char a, int fieldWidth = 0, char fillChar = ' ') const;
   dsTSTRING         Arg            (float a, int fieldWidth = 0, int prec = -1, char fmt = 'f', char fillChar = ' ') const;
   dsTSTRING         Arg            (int a, int fieldWidth = 0, char fmt = 'd', char fillChar = ' ') const;
   dsTSTRING         Arg            (UINT a, int fieldWidth = 0, char fmt = 'd', char fillChar = ' ') const;

protected:
   void              Insert         (int pos, int len);
   FORCE_INLINE void AttachBuffer   (const BUFFER *pBuf);

   int               GetBufMemSize     (int strLen);
   int               GetStrLenFromMem  (int memSize);
   BUFFER*           AllocBuffer       (int maxStrLen, int strLen);
   BUFFER*           ReallocBuffer     (BUFFER *pBuf, int maxStrLen, int strLen);
   void              ReleaseBuffer     (BUFFER* pBuf);

   BUFFER*           UnsafeAllocBuffer (int maxStrLen, int strLen);
   FORCE_INLINE void UnsafeAttachBuffer(const BUFFER *pBuf);
   void              UnsafeInit        (const CH *str, int strLen = -1, int addStrLen = 0);
   FORCE_INLINE void UnsafeInitEmpty   ();

private:
   BUFFER            *pBuffer;
};

#if AP_ENABLE_PROCESSOR_LOCK

template <class CH>
AP_DEFINE_BINDABLE_TO_PROCESSOR(dsTSTRING<CH>);

#endif // AP_ENABLE_PROCESSOR_LOCK

typedef dsTSTRING<char>    dsSTRING;
typedef dsTSTRING<wchar_t> dsWSTRING;

/**************************************************************************
* equals()                                                                *
*                                                                         *
**************************************************************************/

inline const char * cstr(const char * a) { return a; }
inline const char * cstr(const dsSTRING & a) { return a.CStr(); }

template <class T1, class T2>
inline bool equals(const T1 & a, const T2 & b) { return strcmp(cstr(a), cstr(b)) == 0; }
inline bool equals(const wchar_t * a, const wchar_t * b) { return wcscmp(a, b) == 0; }

template <class T1, class T2>
inline bool equals_i(const T1 & a, const T2 & b) { return stricmp(cstr(a), cstr(b)) == 0; }

template <class T1, class T2>
inline bool equals_n(const T1 & a, const T2 & b, int count_b = -1) { return strncmp(cstr(a), cstr(b), count_b != -1 ? count_b : strlen(cstr(b))) == 0; }

template <class T1, class T2>
inline bool startswith(const T1 & a, const T2 & b, int count_b = -1) { return strncmp(cstr(a), cstr(b), count_b != -1 ? count_b : strlen(cstr(b))) == 0; }

// sprintf to dsSTRING
#define _PR dsSPrintf
dsSTRING __CDECL  dsSPrintf(const char *fmt, ...);
dsSTRING __CDECL  dsSPrintfV(const char *fmt, va_list args);
void __CDECL      dsSPrintfS(dsSTRING &dstStr, const char *fmt, ...);
void __CDECL      dsSPrintfVS(dsSTRING &dstStr, const char *fmt, va_list args);

DWORD             dsStrToFcc(const char *str);
dsSTRING          dsFccToStr(DWORD fcc);

#ifdef __PLUGIN__
#pragma warning(disable : 4530)
#include <iostream>
inline std::ostream & operator << (std::ostream & output, const dsSTRING & val)
{ return output << val.CStr(); }
#endif

template <bool isCaseSensitive> 
struct dsNAME_COMPARE {
	int      Compare     (const char * a, const char * b) const { return strcmp(a, b); }
	int      Compare     (const char * a, const dsSTRING & b) const { return strcmp(a, b.CStr()); }
	int      Compare     (const dsSTRING & a, const char * b) const { return strcmp(a.CStr(), b); }
	int      Compare     (const dsSTRING & a, const dsSTRING & b) const { return strcmp(a.CStr(), b.CStr()); }
};

template <> 
struct dsNAME_COMPARE<false> {
	int      Compare     (const char * a, const char * b) const { return stricmp(a, b); }
	int      Compare     (const char * a, const dsSTRING & b) const { return stricmp(a, b.CStr()); }
	int      Compare     (const dsSTRING & a, const char * b) const { return stricmp(a.CStr(), b); }
	int      Compare     (const dsSTRING & a, const dsSTRING & b) const { return stricmp(a.CStr(), b.CStr()); }
};

template <class T, bool isCaseSensitive = true, bool isVariable = false>
struct dsNAME_CMP : dsNAME_COMPARE<isCaseSensitive> {

	int      cmp         (const T * a, const T * b) const { return Compare(a->GetName(), b->GetName()); }
	int      cmp         (const T * a, const char * name) const { return Compare(a->GetName(), name); }
	int      cmp         (const char * name, const T * a) const { return Compare(name, a->GetName()); }
	int      cmp         (const T * a, const dsSTRING & name) const { return Compare(a->GetName(), name); }
	int      cmp         (const dsSTRING & name, const T * a) const { return Compare(name, a->GetName()); }

	int      cmp         (const T & a, const T & b) const { return Compare(a.GetName(), b.GetName()); }
	int      cmp         (const T & a, const char * name) const { return Compare(a.GetName(), name); }
	int      cmp         (const char * name, const T & a) const { return Compare(name, a.GetName()); }
	int      cmp         (const T & a, const dsSTRING & name) const { return Compare(a.GetName(), name); }
	int      cmp         (const dsSTRING & name, const T & a) const { return Compare(name, a.GetName()); }
};                      

template <class T, bool isCaseSensitive>
struct dsNAME_CMP<T, isCaseSensitive, true> : dsNAME_COMPARE<isCaseSensitive> {

   int      cmp         (const T * a, const T * b) const { return Compare(a->name, b->name); }
   int      cmp         (const T * a, const char * name) const { return Compare(a->name, name); }
   int      cmp         (const char * name, const T * a) const { return Compare(name, a->name); }
   int      cmp         (const T * a, const dsSTRING & name) const { return Compare(a->name, name); }
   int      cmp         (const dsSTRING & name, const T * a) const { return Compare(name, a->name); }

   int      cmp         (const T & a, const T & b) const { return Compare(a.name, b.name); }
   int      cmp         (const T & a, const char * name) const { return Compare(a.name, name); }
   int      cmp         (const char * name, const T & a) const { return Compare(name, a.name); }
   int      cmp         (const T & a, const dsSTRING & name) const { return Compare(a.name, name); }
   int      cmp         (const dsSTRING & name, const T & a) const { return Compare(name, a.name); }
};                      

struct m3dV;
struct m3dVTX;
struct m4dV;
class m3dMATR;
class m3dBOX;
class m3dOBB;

namespace ds {

/**************************************************************************
* ToString()                                                              *
*                                                                         *
**************************************************************************/
dsSTRING ToString(int val);
dsSTRING ToString(long val);
dsSTRING ToString(unsigned int val);
dsSTRING ToString(unsigned long val);
dsSTRING ToString(float val);
dsSTRING ToString(const m3dV& val);
dsSTRING ToString(const m3dVTX& val);
dsSTRING ToString(const m4dV& val);
dsSTRING ToString(const m3dMATR& matr);
dsSTRING ToString(const m3dBOX& box);
dsSTRING ToString(const m3dOBB& box);
dsSTRING ToString(const void * ptr);

inline dsSTRING ToString(const char * val)
{ return val; }

inline const dsSTRING & ToString(const dsSTRING & val)
{ return val; }

/// Make readable string for byte values. Example 123456 -> "120.7 Kb"
dsSTRING __CDECL MakeBytesReadable(UINT value);

/// Make readable string for data transfer rates. Example 123456 -> "120.7 Kbps"
dsSTRING __CDECL MakeRateReadable(float value);

/// Table making tools

/** Adjusts string to specified length. If given string is greater 
   than given length it will be truncated. If string is less 
   than given length spaces will be appended at the END of the string.
*/
dsSTRING __CDECL MakeAlignedStrL(const char *str, UINT fieldLen, bool cut = false);

/** Adjusts string to specified length. If given string is greater 
   than given length it will be truncated. If string is less 
   than given length spaces will be appended at the FRONT of the string.
*/
dsSTRING __CDECL MakeAlignedStrR(const char *str, UINT fieldLen, bool cut = false);

#define _STR(X) (ds::STRING_BUILDER() << X).ToString()

class STRING_BUILDER {
public:
   STRING_BUILDER(void)
      : source()
   {}

   STRING_BUILDER(dsSTRING & source)
      : source(&source)
   {}

   STRING_BUILDER(const STRING_BUILDER & p)
      : source(p.source)
      , text(p.text)
   {}

   template <class T>
   STRING_BUILDER & operator << (const T & p) {
      if (source != NULL) {
         *source += ds::ToString(p);
      } else {
         text += ds::ToString(p);
      }

      return *this;
   }

   const dsSTRING & ToString(void) const {
      return source ? *source : text;
   }

private:
   dsSTRING *   source;
   dsSTRING     text;
};

};

#include "ds_string.hpp"

#endif


